;;************************************************************************
;; histfrq3.lsp 
;; contains code for setbins and for plot help
;;************************************************************************

(defmeth histofreq-proto :set-binwidth (val)
     (send self :binwidth val)
     (send self :recompute)
     (send self :make-plot))


(defmeth histofreq-proto :make-freqpoly ()
(let* ((lowest-mid-point (send self :lowest-bin-value))
       (cur-data (send self :data))
       (curvar (send self :current-varnum))
       (maxval (max cur-data))
       
       (binwidth (send self :binwidth))
       (minval (- lowest-mid-point (/ binwidth 2)))
       (num-bins (first (multiple-value-list (ceiling (/ (- maxval minval) binwidth)))))
       (seqwidth (repeat binwidth num-bins))
       (whole-seqwidth (combine minval seqwidth))
       (cur-cut-points (cumsum whole-seqwidth)))
  (when (send self :histo)
        (send self :toggle-histo-poly)
        ;(send self :toggle-full-option)
        )
  (send self :cut-points cur-cut-points)
  (send self :counts (send self :contvar-make-counts cur-cut-points cur-data))
  (send self :mid-points (send self :make-mid-points))
  (send self :make-plot)
  (send self :new-x-axis)
  ))


(defmeth histofreq-proto :make-barchart ()
  (let* ((cur-data (send self :data))
         (cur-cut-points (send self :cutpoints-barchart cur-data)))
    (send self :cut-points cur-cut-points)
    (send self :counts
          (send self :contvar-make-counts cur-cut-points cur-data))
    (send self :mid-points (send self :make-mid-points))
    (send self :start-buffering)

    (cond 
      ((send self :poly)
       (send self :toggle-histo-poly)
       (send self :toggle-full-option))
      ((send self :histo)
       (if (not (send self :full-hist))
           (send self :toggle-full-option))))
    (send self :toggle-plot)
    (send self :poly nil)
    (send self :with-points nil)
    (send self :make-plot)
    (send self :new-x-axis)
    (send self :bar-chart-legend2 t)
    (send self :buffer-to-screen)
))


(defmeth histofreq-proto :cutpoints-barchart (data)
  (let* ((index (remove-duplicates (sort-data data)))
        (mindif (send self :spacing index))
        (up-cutpoints (+ index (/ mindif 4)))
        (low-cutpoints (- index (/ mindif 4)))
        (buffer))
  (dotimes (i (length index) buffer)
           (setf low (select low-cutpoints i))
           (setf up (select up-cutpoints i))
           (setf buffer (combine buffer low up)))
    (setf cutpoints-barchart (remove-duplicates (rest buffer)))))


(defmeth histofreq-proto :contvar-make-counts (cutpoints data)
    (let* ((buffer)
          (data (sort-data data))
          (index cutpoints))
      (dotimes (p (- (length index) 1) buffer)
          (setf i (select index p))
          (setf j (select index (+ 1 p)))
          (setf freq (count-if #'(lambda (x) (and (< i x) (<= x j))) data))
;which are the criteria for setting the intervals limits?
          (setf buffer (combine buffer freq)))
      (setf cat-counts (rest buffer))))


(defmeth histofreq-proto :set-bins ()
  ;(setf hf (aref (send  *current-spreadplot* :plot-matrix) 1 2))
  (setf hf self)
  (let* ((text (send text-item-proto :new (format nil "CUSTOM DESIGN YOUR OWN FREQUENCY or BAR CHART")))
         (left-text (send text-item-proto :new (format nil "To Make Your Own~%FREQ CHART: Enter values below~%BAR  CHART: Enter blanks below")))
         (tit-w (send text-item-proto :new "Bin width:"))
         (tit-l (send text-item-proto :new "Lowest Midpoint:"))
         (set-l (send edit-text-item-proto :new
                      (format nil "~,2f" (first (send hf :mid-points)))
                      :text-length 6
                      :action #'(lambda () (send hf :set-l))))
         (set-w (send edit-text-item-proto :new
                      (format nil "~,2f" (send hf :binwidth))
                              :text-length 6
                              :action #'(lambda () (send hf :set-w))))
                
                
         (range (send text-item-proto :new (format nil "~%Customize the X-Axis~%by Entering Values Below:")))
         (low  (send text-item-proto :new "X-Axis Minimum"))
         (high (send text-item-proto :new "X-Axis Maximum"))
         
         (xmin-item (send edit-text-item-proto :new
                          (format nil "~8,2f" (first (send hf :range 0)))
                          :text-length 8
                          :action #'(lambda () (send hf :set-xmin))))
         (xmax-item (send edit-text-item-proto :new
                          (format nil "~8,2f" (second (send hf :range 0)))
                          :text-length 8
                          :action #'(lambda () (send hf :set-xmax))))
         (tics (send text-item-proto :new "Number of Tics"))
         (ntic-item (send edit-text-item-proto :new
                          (format nil "~8d" (third (send hf :x-axis)))
                          :text-length 8
                          :action #'(lambda () (send hf :set-ntic))))
         (button (send button-item-proto :new "Make Chart"
                       :action #'(lambda ()
                   (cond 
                     ((or (not (send hf :binwidth))
                          (not (send hf :lowest-bin-value))))
                     ((and (equal " " (send hf :binwidth))
                           (equal " " (send hf :lowest-bin-value)))
                      (send hf :make-barchart))
                     ((or (not (numberp (send hf :binwidth)))
                          (not (numberp (send hf :lowest-bin-value))))
                      (message-dialog "The binwidth and lowest midpoint~%must each be a number or a blank."))
                      (t (send hf :make-freqpoly) ))))) 
         (help (send button-item-proto :new "Help" 
                      :action #'(lambda () (send hf :plot-help))))
         )
    (defmeth hf :set-w ()
      (send hf :binwidth 
            (read-from-string (send set-w :text) nil " "))
      (print (send hf :binwidth)))
    (defmeth hf :set-l ()
      (send hf :lowest-bin-value 
            (read-from-string (send set-l :text) nil " "))
      (print (send hf :lowest-bin-value)))
    (defmeth hf :set-xmin ()
      (let ((x-min-val (first (send hf :range 0))))
        (send hf :new-xmin-value 
              (read-from-string (send xmin-item :text) nil x-min-val))
        (print (send hf :new-xmin-value))
        ))
    (defmeth hf :set-xmax ()
      (let ((x-max-val (second (send hf :range 0))))
        (send hf :new-xmax-value
              (read-from-string (send xmax-item :text) nil x-max-val))
        (print (send hf :new-xmax-value))
        ))
    (defmeth hf :set-ntic ()
      (let ((x-ntic (third (send hf :x-axis))))
        (send hf :new-ntic-value   
              (read-from-string (send ntic-item :text) nil x-ntic))))
   (defmeth hf :new-x-axis ()
      (send hf :range 0 (send hf :new-xmin-value) (send hf :new-xmax-value))
      (send hf :x-axis t t (send hf :new-ntic-value)))
    (defmeth hf :initialize-values ()
      (print "initialize")
      (send hf :set-l)
      (send hf :set-w)
      (send hf :set-xmin)
      (send hf :set-xmax)
      (send hf :set-ntic))
    (send dialog-proto :new
          (list text
                (list 
                 (list left-text
                  (list (list tit-w  
                              tit-l )
                        (list set-w 
                              set-l ))
                  (list button help))
                 (list range
                       (list (list low 
                                   high
                                   tics)
                             (list xmin-item 
                                   xmax-item
                                   ntic-item)))))
          :default-button button
          :location '(315 130))
    (send hf :initialize-values)
    ))


;==================================================================
;histofreq plot help
;==================================================================


(defmeth histofreq-proto :plot-help (&key (flush t) (type nil)) 
  (let ((overlay (first (send self :slot-value (quote overlays))))
        (histo (send self :histo))
        (full-hist (send self :full-hist))
        (poly (send self :poly))
        (type)
        (w))
    (flet ((plottype ()
             (if histo (if full-hist "Histogram" "Hollow Histogram")
                 "Frequency Polygon"))
           (plotinterp ()
             (if histo (if full-hist "bar with a red dot in the middle. The bar's" "horizontal line with a red dot in the middle. The line's")
                 (if (send self :poly)
                     "peak or valley in a jagged line connecting together several red dots. The red dots are located so that their" )))
         )
      (setf w (plot-help-window (strcat "Help for " (plottype)) :flush flush))
      (paste-plot-help (format nil 
                             "The ~a is designed to show you the shape of a variable's distribution. It does this by breaking the range of the variable's values into equal-sized intervals called BINS. It then displays the number of observations that fall into the interval (are in the BIN) as a ~a height is proportional to the frequency in the interval. The higher the ~a, the greater the frequency in the bin.~2%" (plottype) (plotinterp)
                               (if (or histo full-hist) "bar" "red dot")) w)
(paste-plot-help (format nil 
"The red dots are located above the midpoint of each bin, and to the right of the frequency of the bin. You can brush your cursor over the red dots to see the frequency and midpoint of the bin.~2%") w)
(paste-plot-help (format nil 
"Unfortunately, the ~a is notorious for conveying an impression of the shape of the variable's distribution that is strongly dependent on the number of bins choosen. Changing the number of bins may radically change the apparent shape of the distribution. Even more unfortunately, there is no entirely satisfactory way to solve this problem.~2%"
                         (plottype)) w)
(paste-plot-help (format nil "For this reason there are two buttons on the graph that help you control the number of bins. These are the BINWIDTH button at the bottom and the NEWBINS button at the top.~2%The BINWIDTH buttons can be used to dynamically change the bin widths, and, consequently, the number of bins. By putting your cursor on the button and holding your mouse button down, these buttons allow you to  watch the graph change in an animated way. Clicking on the NEWBINS button gives you a dialog box that lets you customize the bin widths and midpoints (as well as the x-axis) to get a better distribution.~2%We recommend that you first use the BINWIDTH buttons to get a better impression of the distribution's shape, and then the NEWBINS button to choose a \"nice\" bin width and midpoint. \"Nice\" means that the distribution adequately portrays the shape of the distribution, and the bin widths, midpoints and axis details use sensible numbers.~2%"
                         (plottype)) w)
(paste-plot-help (format nil
"You can use the PLOTS button at the bottom of the graph to cycle through three ways of plotting the frequency information: Histogram, Hollow Histogram and Frequency Polygon. Unfortunately, all three of these formats suffer from the same binning problem discussed above.~2%") w)
    (when (send overlay :curves)
          (paste-plot-help
           (format nil
                   "The CURVES button can be used to add or remove several different distribution curves, including the normal distribution and several curves called \"kernel density distribution curves\". The kernel density distribution curves provide several alternate ways of approximating the shape of the population distribution. If the kernel density curves roughly approximate the normal distribution curve, then the variable's distribution approximates normality.~2%")))
    (when (send overlay :normal)
          (paste-plot-help 
           (format nil 
                   "The NORMAL button can be used to add or remove a normal distribution with the variable's mean and variance. If the histogram roughly approximates the normal distribution, then the variable's distribution approximates normality.~2%")))

(when (send overlay :new-x)
(paste-plot-help  (format nil "When you click on the X button at the top of the graph you will be presented with a list of variables to display (if there are only two variables, it will switch to the other variable). Clicking on a variable will change the plot to display that variable's ~a."(plottype)) w))
(when (send overlay :new-y)
(paste-plot-help  (format nil "When you click on the Y button at the top of the graph the y axis will switch between frequency and probability.~2%") w))
(paste-plot-help (format nil "Finally, when you click on the DATA button at the bottom of the graph, you will create a cumulative frequency table dataobject. It contains several variables specifying frequencies and cumulative frequencies, percentages and cumulative percentages, and limits and midpoints."))

(show-plot-help)
    )))


;;############################################################################
;; bar graph object prototype (inherits from histofreq)
;;############################################################################

(defun bar-chart2 (&rest args)
"Alias for bar-graph" 
  (apply #'bar-graph args))

(defun bar-graph2 (data &key freqs freq prob  new-x (new-y t)
                       (legend1 (send current-object :name))
                       (legend2 "Bar Graph") (title "Bar Graph")
                       variable-labels category-labels
                       location size (auto-sort nil) (go-away t) (show t))
"BAR-GRAPH2 makes bar-graphs from category data using the de Leeuw/Bond histofreq algorithm. 
Required Arg: DATA, which is either a list (or vector) or a list of lists (or vectors) of either 1) the values or 2) the category frequencies of several variables. Must be variable values unless FREQS is true.  The first variable is plotted initially. Y-axis is probabilities if PROB is T, frequencies otherwise; X-axis is labeled with VARIABLE-LABELS. Points are labeled with :CATEGORY-LABELS. When NEW-X is T then a new-x button is put on tool-bar (default nil).  When NEW-Y is T then a new-y button is put on the tool-bar to control y-axis representing frequencies or probabilities (default t).  Has the standard arguments TITLE LOCATION SIZE (GO-AWAY T) (SHOW T) LEGEND1 and LEGEND2."
  (if (or freq freqs) (setf freqs t))
  (send bar-graph-proto :new data :freqs freqs :prob prob 
        :new-x new-x :new-y new-y :legend1 legend1 :legend2 legend2 
        :variable-labels variable-labels :category-labels category-labels
        :title title :location location :size size :go-away go-away :auto-sort auto-sort
        :show show ))

(defproto bar-graph-proto '() () histofreq-proto )

(defmeth bar-graph-proto :isnew 
  (data &key freqs prob new-x new-y legend1 legend2 
        title location size go-away show variable-labels category-labels auto-sort)
  (call-next-method data :freqs freqs :bar t :prob prob :binwidth '(.5)
        :title title  :location location :size size :go-away go-away 
        :show show :variable-labels variable-labels :category-labels category-labels
        :new-x new-x :new-y new-y :legend1 legend1 :legend2 legend2 :sort nil
        :bottom-tool-bar nil :auto-sort auto-sort))


(defmeth bar-graph-proto :plot-help (&key (flush t)) 
  (let ((overlay (first (send self :slot-value (quote overlays))))
        (w))
    (setf w (plot-help-window (strcat "Help for Bar Graphs") :flush flush))
    (paste-plot-help (format nil  "BAR GRAPHS ~2%A Bar Graph is used to portray the (grouped) frequency distribution of a variable at the Nominal level of measurement. Such variables are called Category Variables in ViSta. It consists of vertical bars drawn above categories such that~%1.The height of the bar corresponds to the frequency ~%2.The bars are separated by empty space since the nominal level of measurement has separate, discrete categories.~2%") w)
(show-plot-help)))